﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using VMS.FTS.APPPUSH.Log;
using VMS.FTS.APPPUSH.Utils;
using VMS.FTS.APPPUSH.Device;
using VMS.FTS.APPPUSH.WCFClients;
using VMS.FTS.APPPUSH.VmsCenter;
using System.IO;
using System.Xml.Serialization;
using VMS.FTS.APPPUSH.Utils.FontData;

namespace VMS.FTS.APPPUSH
{
    public class VMSCoreServices  : IDisposable
    {
        static DeviceManager DeviceMng;
        public static ObservableQueue<VmsCommand> DataSpool;
#if !WCF_ENABLED
        static WCFClient Client;
#endif

        #region | Singleton construction |
        private static readonly Lazy<VMSCoreServices> lazy =
            new Lazy<VMSCoreServices>(() => new VMSCoreServices());

        public static VMSCoreServices Instance { get { return lazy.Value; } }
        #endregion

        #region | Constructor |
        public VMSCoreServices()
        {
            // Init logging system
            Logger.Instance.LogLevel = EnumUtils.GetEnumDescription(LOG_LEVEL.ALL);
            //Logger.Instance.LogDir = WORKING_DIR + "\\LOG\\";

            // Init Device manager system
            //DeviceManager.Instance.DATABASE_NAME = WORKING_DIR + "\\DB\\LocalDB.xml";
            DeviceManager.Instance.InitNetwork();
            DeviceMng = DeviceManager.Instance;
            string fontPath = Properties.Settings.Default.WORKING_DIRECTORY +
                Properties.Settings.Default.FONT_DIRECTORY +
                Properties.Settings.Default.FONT_NAME;

            ElementsMap mapIns = ElementsMap.Instance();
            mapIns.LoadFromFile(fontPath);
            

            DataSpool = new ObservableQueue<VmsCommand>();
#if !WCF_ENABLED
            // Init WCFclient routies
            Client = new WCFClient(DeviceManager.Instance.GetAvaibleDevice());
            Client.DataSpool = DataSpool;
#endif
            SpoolerThread = new Thread(new ParameterizedThreadStart(SpoolerThreadHandler));
            this.LOG("Starting spooler thread", LOG_LEVEL.INFO);
            // Waiting for new client connected and start new thread
            SpoolerThread.Start();
#if !WCF_ENABLED
            Client.Start();
#endif

        }

        ~VMSCoreServices()
        {
            LOG("Dispose Core services object...", LOG_LEVEL.DEBUG);
            Dispose();
        }
        #endregion

        #region | Spooler thread handler |
        Thread SpoolerThread;
        bool flag = true;
        private void SpoolerThreadHandler(object obj)
        {
            while (true)
            {
                // check LOG avaible
                if (DataSpool.Count > 0 && flag)
                {
                    // create new temp log entry
                    List<VmsCommand> tempEntry = new List<VmsCommand>();
                    lock (DataSpool)
                    {
                        // copy log entry
                        tempEntry.AddRange(DataSpool);
                        // clear log
                        DataSpool.Clear();
                    }

                    // process command file
                    foreach (VmsCommand cmd in tempEntry)
                    {
                        LOG(getCommandInfo(cmd), LOG_LEVEL.DEBUG);
                        VMSDevice dev = DeviceMng.GetDeviceByID(cmd.VmsId);
                        if (dev == null)
                        {
                            LOG(string.Format("Can not find device: {0} for command", cmd.VmsId),
                                LOG_LEVEL.ERROR);
                            continue;
                        }

                        if (cmd.GetType() == typeof(VmsSystemCommand) &&
                        ((VmsSystemCommand)cmd).CommandType == VmsSystemCommandEnum.COMMAND_GET_STATUS)
                        {
                            dev.UpdateDeviceStatus();
#if !WCF_ENABLED
                            bool ret = Client.SendStatus(dev.ID, dev.Status);
#endif
                        }
                        else
                        {
                            try
                            {
                                bool ret = dev.ProcessCommand(cmd);
                            }
                            catch (VMSLayoutNotFoundException e)
                            {
#if !WCF_ENABLED
                                // request layout from center
                                VmsLayout layout = Client.RequestLayout(e.LayoutID);
                                if (layout != null)
                                {
                                    continue;
                                }
#endif
                            }
                        }
                    }
                }
            }
        }

        #endregion
 
        #region | logging |
        protected void LOG(string message, LOG_LEVEL level)
        {
            Logger.Instance.append(this.GetType().Name, message, level);
#if DEBUG
            ///Debug.WriteLine (LOG_TAG, message, level);
            if (level == LOG_LEVEL.ERROR)
                Console.ForegroundColor = ConsoleColor.Red;
            else if (level == LOG_LEVEL.INFO)
                Console.ForegroundColor = ConsoleColor.Cyan;
            else if (level == LOG_LEVEL.DEBUG)
                Console.ForegroundColor = ConsoleColor.Magenta;
            else
                Console.ForegroundColor = ConsoleColor.White;
            if (Console.CursorLeft != 0)
                Console.WriteLine();
            Console.Write(string.Format("{0,-9}| {1,-6}| {2,-20}| {3}",
                                DateTime.Now.ToString("hh:mm:ss"), level, "Main", message));
#endif
        }
        protected void LOG(string function, string message, LOG_LEVEL level)
        {
            message = string.Format("{0,-20}| {1}", function, message);
            Logger.Instance.append(this.GetType().Name, message, level);
#if DEBUG
            ///Debug.WriteLine (LOG_TAG, message, level);
            if (level == LOG_LEVEL.ERROR)
                Console.ForegroundColor = ConsoleColor.Red;
            else if (level == LOG_LEVEL.INFO)
                Console.ForegroundColor = ConsoleColor.Cyan;
            else if (level == LOG_LEVEL.DEBUG)
                Console.ForegroundColor = ConsoleColor.Magenta;
            else
                Console.ForegroundColor = ConsoleColor.White;
            if (Console.CursorLeft != 0)
                Console.WriteLine();
            Console.Write(string.Format("{0,-9}| {1,-6}| {2,-20}| {3}",
                                DateTime.Now.ToString("hh:mm:ss"), level, "Main", message));
#endif
        }
        #endregion 

        #region | command info |
        string getCommandInfo(VmsCommand cmd)
        {
            StringBuilder sb = new StringBuilder();
            sb.AppendFormat("\n{0,10}Command Info:\n{1,15}{2}\n{3,15}{4} ", "",
                "ID: ", cmd.Id,
                "VMS ID: ", cmd.VmsId
                );

            if (cmd.GetType() == typeof(VmsSystemCommand))
            {
                VmsSystemCommand systemCommand = (VmsSystemCommand)cmd;
                sb.AppendFormat("Command type: " + systemCommand.CommandType.ToString());
                return sb.ToString();
            }
            foreach (VmsCommandPayload payload in cmd.Payloads)
            {
                if (payload.GetType() == typeof(VmsControlCommandPayload))
                {
                    VmsControlCommandPayload p = (VmsControlCommandPayload)payload;
                    sb.AppendFormat("\n{0,20}{1,3}{2,15}{3}{4,15}{5}",
                        "Layout ID: ", p.LayoutId,
                        "Popup ID: ", p.PopupId,
                        "Playlist ID: ", p.Playlist.Id);

                    foreach (VmsPlaylistItem item in p.Playlist.Items)
                    {
                        if (item.GetType() == typeof(VmsImagePlaylistItem))
                        {
                            sb.AppendFormat("\n{0,25}ImageID: {1} Effect: {2} Duration: {3}", "",
                                ((VmsImagePlaylistItem)item).ImageID,
                                ((VmsImagePlaylistItem)item).Effect,
                                ((VmsImagePlaylistItem)item).Duration);
                        }
                    }
                }
            }
            return sb.ToString();
        }
        #endregion

        #region | Disposable |
        public void Dispose()
        {
#if !WCF_ENABLED
            Client.Stop();
#endif
            flag = false;
            if (SpoolerThread != null)
                SpoolerThread.Abort();

            DeviceMng.StopServer();
        }
        #endregion

        public VMSDevice getDevice(string devID)
        {
            return DeviceMng.GetDeviceByID(devID);
        }

        public void PrintDevices(bool showOffline = false)
        {
            StringBuilder logContent = new StringBuilder();

            logContent.Append("List all avaible devices: ");
            if (showOffline)
                logContent.Append("(Online only)");

            logContent.AppendFormat("\n{0,-10}| {1,16}| {2,-10}", "Device ID","IMEI","Status");
            foreach (VMSDevice dev in DeviceMng.Devices)
            {
                logContent.AppendFormat("\n{0,-10}| {1,16}| {2,-10}", dev.ID, dev.IMEI, dev.Status.Connected);
            }

            LOG(logContent.ToString()+"\r\n", LOG_LEVEL.INFO);
        }

        public void FontDump(ushort fontSize,bool bitmap = false)
        {
            string fontPath = Properties.Settings.Default.WORKING_DIRECTORY +
       Properties.Settings.Default.FONT_DIRECTORY +
       Properties.Settings.Default.FONT_NAME;
            if (File.Exists(fontPath))
                FontUtils.Dump(fontPath, fontSize, bitmap);
            else
            {
                Console.WriteLine("Can't load font resource");
            }
        }
    }
}
